1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.base;
18  
19  import static com.google.common.truth.Truth.assertThat;
20  
21  import com.google.common.annotations.GwtCompatible;
22  import com.google.common.collect.ImmutableList;
23  import com.google.common.collect.ImmutableMap;
24  
25  import junit.framework.TestCase;
26  
27  import java.util.Collection;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  
32  /**
33   * @author Julien Silland
34   */
35  @GwtCompatible(emulated = true)
36  public class SplitterTest extends TestCase {
37  
38    private static final Splitter COMMA_SPLITTER = Splitter.on(',');
39  
40    public void testSplitNullString() {
41      try {
42        COMMA_SPLITTER.split(null);
43        fail();
44      } catch (NullPointerException expected) {
45      }
46    }
47  
48    public void testCharacterSimpleSplit() {
49      String simple = "a,b,c";
50      Iterable<String> letters = COMMA_SPLITTER.split(simple);
51      assertThat(letters).iteratesAs("a", "b", "c");
52    }
53  
54    /**
55     * All of the infrastructure of split and splitToString is identical, so we
56     * do one test of splitToString. All other cases should be covered by testing
57     * of split.
58     *
59     * <p>TODO(user): It would be good to make all the relevant tests run on
60     * both split and splitToString automatically.
61     */
62    public void testCharacterSimpleSplitToList() {
63      String simple = "a,b,c";
64      List<String> letters = COMMA_SPLITTER.splitToList(simple);
65      assertThat(letters).iteratesAs("a", "b", "c");
66    }
67  
68    public void testToString() {
69      assertEquals("[]", Splitter.on(',').split("").toString());
70      assertEquals("[a, b, c]", Splitter.on(',').split("a,b,c").toString());
71      assertEquals("[yam, bam, jam, ham]", Splitter.on(", ").split("yam, bam, jam, ham").toString());
72    }
73  
74    public void testCharacterSimpleSplitWithNoDelimiter() {
75      String simple = "a,b,c";
76      Iterable<String> letters = Splitter.on('.').split(simple);
77      assertThat(letters).iteratesAs("a,b,c");
78    }
79  
80    public void testCharacterSplitWithDoubleDelimiter() {
81      String doubled = "a,,b,c";
82      Iterable<String> letters = COMMA_SPLITTER.split(doubled);
83      assertThat(letters).iteratesAs("a", "", "b", "c");
84    }
85  
86    public void testCharacterSplitWithDoubleDelimiterAndSpace() {
87      String doubled = "a,, b,c";
88      Iterable<String> letters = COMMA_SPLITTER.split(doubled);
89      assertThat(letters).iteratesAs("a", "", " b", "c");
90    }
91  
92    public void testCharacterSplitWithTrailingDelimiter() {
93      String trailing = "a,b,c,";
94      Iterable<String> letters = COMMA_SPLITTER.split(trailing);
95      assertThat(letters).iteratesAs("a", "b", "c", "");
96    }
97  
98    public void testCharacterSplitWithLeadingDelimiter() {
99      String leading = ",a,b,c";
100     Iterable<String> letters = COMMA_SPLITTER.split(leading);
101     assertThat(letters).iteratesAs("", "a", "b", "c");
102   }
103 
104   public void testCharacterSplitWithMulitpleLetters() {
105     Iterable<String> testCharacteringMotto = Splitter.on('-').split(
106         "Testing-rocks-Debugging-sucks");
107     assertThat(testCharacteringMotto).iteratesAs(
108         "Testing", "rocks", "Debugging", "sucks");
109   }
110 
111   public void testCharacterSplitWithMatcherDelimiter() {
112     Iterable<String> testCharacteringMotto = Splitter
113         .on(CharMatcher.WHITESPACE)
114         .split("Testing\nrocks\tDebugging sucks");
115     assertThat(testCharacteringMotto).iteratesAs(
116         "Testing", "rocks", "Debugging", "sucks");
117   }
118 
119   public void testCharacterSplitWithDoubleDelimiterOmitEmptyStrings() {
120     String doubled = "a..b.c";
121     Iterable<String> letters = Splitter.on('.')
122         .omitEmptyStrings().split(doubled);
123     assertThat(letters).iteratesAs("a", "b", "c");
124   }
125 
126   public void testCharacterSplitEmptyToken() {
127     String emptyToken = "a. .c";
128     Iterable<String> letters = Splitter.on('.').trimResults()
129         .split(emptyToken);
130     assertThat(letters).iteratesAs("a", "", "c");
131   }
132 
133   public void testCharacterSplitEmptyTokenOmitEmptyStrings() {
134     String emptyToken = "a. .c";
135     Iterable<String> letters = Splitter.on('.')
136         .omitEmptyStrings().trimResults().split(emptyToken);
137     assertThat(letters).iteratesAs("a", "c");
138   }
139 
140   public void testCharacterSplitOnEmptyString() {
141     Iterable<String> nothing = Splitter.on('.').split("");
142     assertThat(nothing).iteratesAs("");
143   }
144 
145   public void testCharacterSplitOnEmptyStringOmitEmptyStrings() {
146     assertThat(Splitter.on('.').omitEmptyStrings().split("")).isEmpty();
147   }
148 
149   public void testCharacterSplitOnOnlyDelimiter() {
150     Iterable<String> blankblank = Splitter.on('.').split(".");
151     assertThat(blankblank).iteratesAs("", "");
152   }
153 
154   public void testCharacterSplitOnOnlyDelimitersOmitEmptyStrings() {
155     Iterable<String> empty = Splitter.on('.').omitEmptyStrings().split("...");
156     assertThat(empty).isEmpty();
157   }
158 
159   public void testCharacterSplitWithTrim() {
160     String jacksons = "arfo(Marlon)aorf, (Michael)orfa, afro(Jackie)orfa, "
161         + "ofar(Jemaine), aff(Tito)";
162     Iterable<String> family = COMMA_SPLITTER
163         .trimResults(CharMatcher.anyOf("afro").or(CharMatcher.WHITESPACE))
164         .split(jacksons);
165     assertThat(family).iteratesAs(
166         "(Marlon)", "(Michael)", "(Jackie)", "(Jemaine)", "(Tito)");
167   }
168 
169   public void testStringSimpleSplit() {
170     String simple = "a,b,c";
171     Iterable<String> letters = Splitter.on(',').split(simple);
172     assertThat(letters).iteratesAs("a", "b", "c");
173   }
174 
175   public void testStringSimpleSplitWithNoDelimiter() {
176     String simple = "a,b,c";
177     Iterable<String> letters = Splitter.on('.').split(simple);
178     assertThat(letters).iteratesAs("a,b,c");
179   }
180 
181   public void testStringSplitWithDoubleDelimiter() {
182     String doubled = "a,,b,c";
183     Iterable<String> letters = Splitter.on(',').split(doubled);
184     assertThat(letters).iteratesAs("a", "", "b", "c");
185   }
186 
187   public void testStringSplitWithDoubleDelimiterAndSpace() {
188     String doubled = "a,, b,c";
189     Iterable<String> letters = Splitter.on(',').split(doubled);
190     assertThat(letters).iteratesAs("a", "", " b", "c");
191   }
192 
193   public void testStringSplitWithTrailingDelimiter() {
194     String trailing = "a,b,c,";
195     Iterable<String> letters = Splitter.on(',').split(trailing);
196     assertThat(letters).iteratesAs("a", "b", "c", "");
197   }
198 
199   public void testStringSplitWithLeadingDelimiter() {
200     String leading = ",a,b,c";
201     Iterable<String> letters = Splitter.on(',').split(leading);
202     assertThat(letters).iteratesAs("", "a", "b", "c");
203   }
204 
205   public void testStringSplitWithMultipleLetters() {
206     Iterable<String> testStringingMotto = Splitter.on('-').split(
207         "Testing-rocks-Debugging-sucks");
208     assertThat(testStringingMotto).iteratesAs(
209         "Testing", "rocks", "Debugging", "sucks");
210   }
211 
212   public void testStringSplitWithDoubleDelimiterOmitEmptyStrings() {
213     String doubled = "a..b.c";
214     Iterable<String> letters = Splitter.on('.')
215         .omitEmptyStrings().split(doubled);
216     assertThat(letters).iteratesAs("a", "b", "c");
217   }
218 
219   public void testStringSplitEmptyToken() {
220     String emptyToken = "a. .c";
221     Iterable<String> letters = Splitter.on('.').trimResults()
222         .split(emptyToken);
223     assertThat(letters).iteratesAs("a", "", "c");
224   }
225 
226   public void testStringSplitEmptyTokenOmitEmptyStrings() {
227     String emptyToken = "a. .c";
228     Iterable<String> letters = Splitter.on('.')
229         .omitEmptyStrings().trimResults().split(emptyToken);
230     assertThat(letters).iteratesAs("a", "c");
231   }
232 
233   public void testStringSplitWithLongDelimiter() {
234     String longDelimiter = "a, b, c";
235     Iterable<String> letters = Splitter.on(", ").split(longDelimiter);
236     assertThat(letters).iteratesAs("a", "b", "c");
237   }
238 
239   public void testStringSplitWithLongLeadingDelimiter() {
240     String longDelimiter = ", a, b, c";
241     Iterable<String> letters = Splitter.on(", ").split(longDelimiter);
242     assertThat(letters).iteratesAs("", "a", "b", "c");
243   }
244 
245   public void testStringSplitWithLongTrailingDelimiter() {
246     String longDelimiter = "a, b, c, ";
247     Iterable<String> letters = Splitter.on(", ").split(longDelimiter);
248     assertThat(letters).iteratesAs("a", "b", "c", "");
249   }
250 
251   public void testStringSplitWithDelimiterSubstringInValue() {
252     String fourCommasAndFourSpaces = ",,,,    ";
253     Iterable<String> threeCommasThenThreeSpaces = Splitter.on(", ").split(
254         fourCommasAndFourSpaces);
255     assertThat(threeCommasThenThreeSpaces).iteratesAs(",,,", "   ");
256   }
257 
258   public void testStringSplitWithEmptyString() {
259     try {
260       Splitter.on("");
261       fail();
262     } catch (IllegalArgumentException expected) {
263     }
264   }
265 
266   public void testStringSplitOnEmptyString() {
267     Iterable<String> notMuch = Splitter.on('.').split("");
268     assertThat(notMuch).iteratesAs("");
269   }
270 
271   public void testStringSplitOnEmptyStringOmitEmptyString() {
272     assertThat(Splitter.on('.').omitEmptyStrings().split("")).isEmpty();
273   }
274 
275   public void testStringSplitOnOnlyDelimiter() {
276     Iterable<String> blankblank = Splitter.on('.').split(".");
277     assertThat(blankblank).iteratesAs("", "");
278   }
279 
280   public void testStringSplitOnOnlyDelimitersOmitEmptyStrings() {
281     Iterable<String> empty = Splitter.on('.').omitEmptyStrings().split("...");
282     assertThat(empty).isEmpty();
283   }
284 
285   public void testStringSplitWithTrim() {
286     String jacksons = "arfo(Marlon)aorf, (Michael)orfa, afro(Jackie)orfa, "
287         + "ofar(Jemaine), aff(Tito)";
288     Iterable<String> family = Splitter.on(',')
289         .trimResults(CharMatcher.anyOf("afro").or(CharMatcher.WHITESPACE))
290         .split(jacksons);
291     assertThat(family).iteratesAs(
292         "(Marlon)", "(Michael)", "(Jackie)", "(Jemaine)", "(Tito)");
293   }
294 
295   // TODO(kevinb): the name of this method suggests it might not actually be testing what it
296   // intends to be testing?
297 
298   public void testSplitterIterableIsUnmodifiable_char() {
299     assertIteratorIsUnmodifiable(COMMA_SPLITTER.split("a,b").iterator());
300   }
301 
302   public void testSplitterIterableIsUnmodifiable_string() {
303     assertIteratorIsUnmodifiable(Splitter.on(',').split("a,b").iterator());
304   }
305 
306   private void assertIteratorIsUnmodifiable(Iterator<?> iterator) {
307     iterator.next();
308     try {
309       iterator.remove();
310       fail();
311     } catch (UnsupportedOperationException expected) {
312     }
313   }
314 
315   public void testSplitterIterableIsLazy_char() {
316     assertSplitterIterableIsLazy(COMMA_SPLITTER);
317   }
318 
319   public void testSplitterIterableIsLazy_string() {
320     assertSplitterIterableIsLazy(Splitter.on(','));
321   }
322 
323   /**
324    * This test really pushes the boundaries of what we support. In general the
325    * splitter's behaviour is not well defined if the char sequence it's
326    * splitting is mutated during iteration.
327    */
328   private void assertSplitterIterableIsLazy(Splitter splitter) {
329     StringBuilder builder = new StringBuilder();
330     Iterator<String> iterator = splitter.split(builder).iterator();
331 
332     builder.append("A,");
333     assertEquals("A", iterator.next());
334     builder.append("B,");
335     assertEquals("B", iterator.next());
336     builder.append("C");
337     assertEquals("C", iterator.next());
338     assertFalse(iterator.hasNext());
339   }
340 
341   public void testFixedLengthSimpleSplit() {
342     String simple = "abcde";
343     Iterable<String> letters = Splitter.fixedLength(2).split(simple);
344     assertThat(letters).iteratesAs("ab", "cd", "e");
345   }
346 
347   public void testFixedLengthSplitEqualChunkLength() {
348     String simple = "abcdef";
349     Iterable<String> letters = Splitter.fixedLength(2).split(simple);
350     assertThat(letters).iteratesAs("ab", "cd", "ef");
351   }
352 
353   public void testFixedLengthSplitOnlyOneChunk() {
354     String simple = "abc";
355     Iterable<String> letters = Splitter.fixedLength(3).split(simple);
356     assertThat(letters).iteratesAs("abc");
357   }
358 
359   public void testFixedLengthSplitSmallerString() {
360     String simple = "ab";
361     Iterable<String> letters = Splitter.fixedLength(3).split(simple);
362     assertThat(letters).iteratesAs("ab");
363   }
364 
365   public void testFixedLengthSplitEmptyString() {
366     String simple = "";
367     Iterable<String> letters = Splitter.fixedLength(3).split(simple);
368     assertThat(letters).iteratesAs("");
369   }
370 
371   public void testFixedLengthSplitEmptyStringWithOmitEmptyStrings() {
372     assertThat(Splitter.fixedLength(3).omitEmptyStrings().split("")).isEmpty();
373   }
374 
375   public void testFixedLengthSplitIntoChars() {
376     String simple = "abcd";
377     Iterable<String> letters = Splitter.fixedLength(1).split(simple);
378     assertThat(letters).iteratesAs("a", "b", "c", "d");
379   }
380 
381   public void testFixedLengthSplitZeroChunkLen() {
382     try {
383       Splitter.fixedLength(0);
384       fail();
385     } catch (IllegalArgumentException expected) {
386     }
387   }
388 
389   public void testFixedLengthSplitNegativeChunkLen() {
390     try {
391       Splitter.fixedLength(-1);
392       fail();
393     } catch (IllegalArgumentException expected) {
394     }
395   }
396 
397   public void testLimitLarge() {
398     String simple = "abcd";
399     Iterable<String> letters = Splitter.fixedLength(1).limit(100).split(simple);
400     assertThat(letters).iteratesAs("a", "b", "c", "d");
401   }
402 
403   public void testLimitOne() {
404     String simple = "abcd";
405     Iterable<String> letters = Splitter.fixedLength(1).limit(1).split(simple);
406     assertThat(letters).iteratesAs("abcd");
407   }
408 
409   public void testLimitFixedLength() {
410     String simple = "abcd";
411     Iterable<String> letters = Splitter.fixedLength(1).limit(2).split(simple);
412     assertThat(letters).iteratesAs("a", "bcd");
413   }
414 
415   public void testLimitSeparator() {
416     String simple = "a,b,c,d";
417     Iterable<String> items = COMMA_SPLITTER.limit(2).split(simple);
418     assertThat(items).iteratesAs("a", "b,c,d");
419   }
420 
421   public void testLimitExtraSeparators() {
422     String text = "a,,,b,,c,d";
423     Iterable<String> items = COMMA_SPLITTER.limit(2).split(text);
424     assertThat(items).iteratesAs("a", ",,b,,c,d");
425   }
426 
427   public void testLimitExtraSeparatorsOmitEmpty() {
428     String text = "a,,,b,,c,d";
429     Iterable<String> items = COMMA_SPLITTER.limit(2).omitEmptyStrings().split(text);
430     assertThat(items).iteratesAs("a", "b,,c,d");
431   }
432 
433   public void testLimitExtraSeparatorsOmitEmpty3() {
434     String text = "a,,,b,,c,d";
435     Iterable<String> items = COMMA_SPLITTER.limit(3).omitEmptyStrings().split(text);
436     assertThat(items).iteratesAs("a", "b", "c,d");
437   }
438 
439   public void testLimitExtraSeparatorsTrim() {
440     String text = ",,a,,  , b ,, c,d ";
441     Iterable<String> items = COMMA_SPLITTER.limit(2).omitEmptyStrings().trimResults().split(text);
442     assertThat(items).iteratesAs("a", "b ,, c,d");
443   }
444 
445   public void testLimitExtraSeparatorsTrim3() {
446     String text = ",,a,,  , b ,, c,d ";
447     Iterable<String> items = COMMA_SPLITTER.limit(3).omitEmptyStrings().trimResults().split(text);
448     assertThat(items).iteratesAs("a", "b", "c,d");
449   }
450 
451   public void testLimitExtraSeparatorsTrim1() {
452     String text = ",,a,,  , b ,, c,d ";
453     Iterable<String> items = COMMA_SPLITTER.limit(1).omitEmptyStrings().trimResults().split(text);
454     assertThat(items).iteratesAs("a,,  , b ,, c,d");
455   }
456 
457   public void testLimitExtraSeparatorsTrim1NoOmit() {
458     String text = ",,a,,  , b ,, c,d ";
459     Iterable<String> items = COMMA_SPLITTER.limit(1).trimResults().split(text);
460     assertThat(items).iteratesAs(",,a,,  , b ,, c,d");
461   }
462 
463   public void testLimitExtraSeparatorsTrim1Empty() {
464     String text = "";
465     Iterable<String> items = COMMA_SPLITTER.limit(1).split(text);
466     assertThat(items).iteratesAs("");
467   }
468 
469   public void testLimitExtraSeparatorsTrim1EmptyOmit() {
470     String text = "";
471     Iterable<String> items = COMMA_SPLITTER.omitEmptyStrings().limit(1).split(text);
472     assertThat(items).isEmpty();
473   }
474 
475   @SuppressWarnings("ReturnValueIgnored") // testing for exception
476   public void testInvalidZeroLimit() {
477     try {
478       COMMA_SPLITTER.limit(0);
479       fail();
480     } catch (IllegalArgumentException expected) {
481     }
482   }
483 
484   private static <E> List<E> asList(Collection<E> collection) {
485     return ImmutableList.copyOf(collection);
486   }
487 
488   public void testMapSplitter_trimmedBoth() {
489     Map<String, String> m = COMMA_SPLITTER
490         .trimResults()
491         .withKeyValueSeparator(Splitter.on(':').trimResults())
492         .split("boy  : tom , girl: tina , cat  : kitty , dog: tommy ");
493     ImmutableMap<String, String> expected =
494           ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy");
495     assertThat(m).isEqualTo(expected);
496     assertThat(asList(m.entrySet())).isEqualTo(asList(expected.entrySet()));
497   }
498 
499   public void testMapSplitter_trimmedEntries() {
500     Map<String, String> m = COMMA_SPLITTER
501         .trimResults()
502         .withKeyValueSeparator(":")
503         .split("boy  : tom , girl: tina , cat  : kitty , dog: tommy ");
504     ImmutableMap<String, String> expected =
505         ImmutableMap.of("boy  ", " tom", "girl", " tina", "cat  ", " kitty", "dog", " tommy");
506 
507     assertThat(m).isEqualTo(expected);
508     assertThat(asList(m.entrySet())).isEqualTo(asList(expected.entrySet()));
509   }
510 
511   public void testMapSplitter_trimmedKeyValue() {
512     Map<String, String> m =
513         COMMA_SPLITTER.withKeyValueSeparator(Splitter.on(':').trimResults()).split(
514             "boy  : tom , girl: tina , cat  : kitty , dog: tommy ");
515     ImmutableMap<String, String> expected =
516         ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy");
517     assertThat(m).isEqualTo(expected);
518     assertThat(asList(m.entrySet())).isEqualTo(asList(expected.entrySet()));
519   }
520 
521   public void testMapSplitter_notTrimmed() {
522     Map<String, String> m = COMMA_SPLITTER.withKeyValueSeparator(":").split(
523         " boy:tom , girl: tina , cat :kitty , dog:  tommy ");
524     ImmutableMap<String, String> expected =
525         ImmutableMap.of(" boy", "tom ", " girl", " tina ", " cat ", "kitty ", " dog", "  tommy ");
526     assertThat(m).isEqualTo(expected);
527     assertThat(asList(m.entrySet())).isEqualTo(asList(expected.entrySet()));
528   }
529 
530   public void testMapSplitter_CharacterSeparator() {
531     // try different delimiters.
532     Map<String, String> m = Splitter
533         .on(",")
534         .withKeyValueSeparator(':')
535         .split("boy:tom,girl:tina,cat:kitty,dog:tommy");
536     ImmutableMap<String, String> expected =
537         ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy");
538 
539     assertThat(m).isEqualTo(expected);
540     assertThat(asList(m.entrySet())).isEqualTo(asList(expected.entrySet()));
541   }
542 
543   public void testMapSplitter_multiCharacterSeparator() {
544     // try different delimiters.
545     Map<String, String> m = Splitter
546         .on(",")
547         .withKeyValueSeparator(":^&")
548         .split("boy:^&tom,girl:^&tina,cat:^&kitty,dog:^&tommy");
549     ImmutableMap<String, String> expected =
550         ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy");
551 
552     assertThat(m).isEqualTo(expected);
553     assertThat(asList(m.entrySet())).isEqualTo(asList(expected.entrySet()));
554   }
555 
556   @SuppressWarnings("ReturnValueIgnored") // testing for exception
557   public void testMapSplitter_emptySeparator() {
558     try {
559       COMMA_SPLITTER.withKeyValueSeparator("");
560       fail();
561     } catch (IllegalArgumentException expected) {
562     }
563   }
564 
565   public void testMapSplitter_malformedEntry() {
566     try {
567       COMMA_SPLITTER.withKeyValueSeparator("=").split("a=1,b,c=2");
568       fail();
569     } catch (IllegalArgumentException expected) {
570     }
571   }
572 
573   public void testMapSplitter_orderedResults() {
574     Map<String, String> m = Splitter.on(',')
575         .withKeyValueSeparator(":")
576         .split("boy:tom,girl:tina,cat:kitty,dog:tommy");
577 
578     assertThat(m.keySet()).iteratesAs("boy", "girl", "cat", "dog");
579     assertThat(m).isEqualTo(
580         ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy"));
581 
582     // try in a different order
583     m = Splitter.on(',')
584         .withKeyValueSeparator(":")
585         .split("girl:tina,boy:tom,dog:tommy,cat:kitty");
586 
587     assertThat(m.keySet()).iteratesAs("girl", "boy", "dog", "cat");
588     assertThat(m).isEqualTo(
589         ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy"));
590   }
591 
592   public void testMapSplitter_duplicateKeys() {
593     try {
594       Splitter.on(',').withKeyValueSeparator(":").split("a:1,b:2,a:3");
595       fail();
596     } catch (IllegalArgumentException expected) {
597     }
598   }
599 }